home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / TPTUTR~1.ZIP / PASCAL18.TXT < prev    next >
Text File  |  1996-03-21  |  17KB  |  516 lines

  1.                        Turbo Pascal for DOS Tutorial
  2.                             by Glenn Grotzinger
  3.           Part 18: Chained or Linked lists, the linked list sort
  4.                 copyright(c) 1995-96 by Glenn Grotzinger
  5.  
  6. Here is a solution from last time...
  7.  
  8. {$M 64000,0,655360}
  9. program part17; uses crt;
  10.  
  11.   type
  12.     arptr = ^atype;
  13.     atype = array[1..15000] of integer;
  14.   var
  15.     a: arptr;
  16.     number: integer;
  17.     c, i, j, PIVOT, t: integer;
  18.     found: boolean;
  19.     location: integer;
  20.     outfile: text;
  21.  
  22.   procedure quicksort(L, R: integer);
  23.     { nothing to say we couldn't sort the data... }
  24.     begin
  25.       if wherex = 79 then
  26.         begin
  27.           gotoxy(1, wherey);
  28.           clreol;
  29.         end
  30.       else
  31.         write(#254);
  32.  
  33.       if L < R then
  34.         begin
  35.           i := L + 1;
  36.           j := R;
  37.           PIVOT := A^[L];
  38.  
  39.           repeat
  40.             while a^[i] <= PIVOT do inc(i);
  41.             while a^[j] > PIVOT do dec(j);
  42.             if i < j then
  43.               begin
  44.                 t := A^[i];
  45.                 A^[i] := a^[j];
  46.                 A^[j] := t;
  47.               end;
  48.           until i > j;
  49.  
  50.           a^[l] := A^[j];
  51.           a^[j] := PIVOT;
  52.  
  53.           quicksort(L, j-1);
  54.           quicksort(i, R);
  55.         end;
  56.     end;
  57.  
  58.   procedure bsearch(number, lowend, highend: integer; var found: boolean);
  59.     var
  60.       midpoint: integer;
  61.     begin
  62.       if lowend > highend then
  63.         found := false
  64.       else
  65.         begin
  66.           midpoint := (lowend + highend) div 2;
  67.           if number = a^[midpoint] then
  68.             begin
  69.               found := true;
  70.               location := midpoint;
  71.             end
  72.           else if number < a^[midpoint] then
  73.             bsearch(number, lowend, midpoint-1, found)
  74.           else if number > a^[midpoint] then
  75.             bsearch(number, midpoint+1, highend, found);
  76.         end;
  77.     end;
  78.  
  79.   begin
  80.     if maxavail - sizeof(a) > 0 then
  81.       new(a)
  82.     else
  83.       begin
  84.         writeln('Out of memory!');
  85.         halt(1);
  86.       end;
  87.     randomize;
  88.     assign(outfile, 'LOCAT2.TXT');
  89.     rewrite(outfile);
  90.  
  91.     for c := 1 to 15000 do
  92.       a^[c] := random(25000) + 1;
  93.  
  94.     quicksort(1, 15000);
  95.  
  96.     for c := 1 to 15000 do
  97.       begin
  98.         number := random(25000) + 1;
  99.         bsearch(number, 1, 15000, found);
  100.         if found then
  101.           writeln(outfile, c, ') ', number, ' was found at position ',
  102.                   location, '.');
  103.       end;
  104.     dispose(a);
  105.     close(outfile);
  106.  
  107. end.
  108.  
  109.  
  110. Now we will discuss the idea of the linked list or chained list.  Basically,
  111. there are 4 types of linked lists that we can discuss, the singularly linked
  112. linear list (SLLL), singularly linked circular list (SLCL), doubly linked
  113. linear list (DLLL), and the doubly linked circular list (DLCL).  I will
  114. use the abbreviations I placed in the parentheses for any future references
  115. to these data types.
  116.  
  117. These are basically the preferred ways to allocate large amounts of storage
  118. space on the heap.  All linked lists are basically describable in the best
  119. analogy as a chain.  They are record structures which have pointers that
  120. interconnect them.  The method that these structures are connected
  121. distinguish the type of linked list it is.  We will look at an example of
  122. the use of SLLL's, observe the advantages of linked lists through what we
  123. do with the example, and study the things to look out for on all 4 types.
  124.  
  125. SLLL Concepts
  126. =============
  127. This is the simplest type, in sense.  It involves a record structure which
  128. is connected in a chain in a linear fashion with one link forward to the
  129. next link.  A sample record structure for an SLLL follows below.
  130.  
  131. type
  132.   nodeptr = ^node;
  133.   nodetype = record
  134.     ourinfo: integer;
  135.     nextnode: nodeptr;
  136.   end;
  137.  
  138. An example of an SLLL conceptually is something like this:
  139.  
  140. NODE-->NODE-->NODE-->NODE-->NODE-->NODE-->NODE-->nil
  141.  
  142. As we remember from earlier, nil is what we set a pointer to, if we do not
  143. want it to point to anything...In the use of an SLLL, it is also what we
  144. will use to indicate whether we are at the end of the list or not.
  145.  
  146. We will see from the slll_demo program that there are several specialized
  147. issues we need to take into consideration with working with any linked or
  148. chained list.
  149.  
  150. 1) We need to make a special case to insert or delete a node from the start
  151. of the list.
  152. 2) We need to be sure to maintain nil pointers in any insert or delete
  153. operation.
  154. 3) NEVER NEVER NEVER WORK DIRECTLY WITH THE HEAD TRACKING POINTER WE
  155. ORIGINALLY ALLOCATE UNLESS WE DESIGN OUR CODE COMPLETELY AROUND RECURSION.
  156. As a result, you will cause what is called a heap leak.  This is where
  157. the pointer loses track of where the data it points to is stored.  Logically,
  158. looking at the model above, if we disconnect one of the pointers, represented
  159. by the arrows, we lose track of the rest of the list, or chain.  Work with
  160. a temporary pointer for each linked list function. What I say by recursion,
  161. you will see later in this document.
  162. 4) With regards to the example I wrote, I tried to demonstrate any and all
  163. functions that we might need with an SLLL.
  164. 5) Pointers that point at nil CAN NOT be deallocated.  You will see this
  165. fact manifest itself by the memory statement at the end being 8 bytes
  166. smaller than it was at the start.
  167.  
  168. SLLLs Demonstrated
  169. ==================
  170. Here is the SLLL_DEMO program.  I will place stop notes in there, as well
  171. as comments.
  172.  
  173. Advantages of linked lists: We will see here, that the data is not static,
  174. we can place data independently at different positions WITHOUT shuffling
  175. data, remove data in the same fashion, and definitely are capable of handling
  176. *A LOT* more data than 64KB, since we only have a 4 byte stub in that area.
  177.  
  178. Take a good look at this program and seek to understand EXACTLY how it works.
  179. As you will remember from last time, a direct assignment to a pointer is
  180. making it point to something while a reference to the pointer (via ^) changes
  181. the contents of the data it points to.  I recommend you draw out what is
  182. going on via pencil and paper, using boxes to represent the records and
  183. arrows representing pointers.  It will help you VASTLY to do this in
  184. understanding what is going on.  Remember a pointer can only point to one
  185. thing at a time.  When you look at this program, seek to answer the
  186. following questions taking any "housekeeping functions" out of consideration:
  187.  
  188. 1) Why is the insert code different than the build code?
  189. 2) Why is the delete code different than the cleanup code?
  190. 3) On the "divisible by 8" search, why is the NEXT node being searched for
  191. this and not the current node?
  192. 4) Why did I say to always use a temporary variable? Or Why does the
  193. statement p := list; always occur?
  194. 5) Observe methods of moving through the list.
  195.  
  196. program slll_demo; uses crt;
  197.  
  198.   { Program written by Glenn Grotzinger for a demonstration of all of the
  199.     functions/uses of a linked list that the author could think of.
  200.     the variable used throughout called p, and sometimes t, are temporary
  201.     variables.
  202.  
  203.     Note: This probably isn't completely optimized. }
  204.  
  205.   type
  206.     nodeptr = ^nodetype;
  207.     nodetype = record
  208.       thenum: integer;
  209.       nextnode: nodeptr;
  210.     end;
  211.  
  212.   var
  213.     list: nodeptr;
  214.  
  215.   procedure buildlist(var list: nodeptr);
  216.  
  217.     { This procedure builds up the list for us. }
  218.  
  219.     var
  220.       p: nodeptr;
  221.       i: integer;
  222.     begin
  223.       new(list);       { This is creating the head of the list }
  224.       list^.thenum := 1;
  225.       p := list;       { Set and move temporary pointer }
  226.       for i := 2 to 18 do
  227.         begin
  228.           new(p^.nextnode);
  229.           p^.nextnode^.thenum := i;
  230.           p := p^.nextnode;
  231.          { p := p^.nextnode advances the temporary pointer to the next node.
  232.            this is a memory storage address or pointer and not a direct
  233.            variable, referencing a node of the linked list.  Anything, in
  234.            reality does not become a pointer until the new function is used. }
  235.         end;
  236.       p^.nextnode := nil;   { set last pointer to nothing }
  237.     end;
  238.  
  239.   procedure writelist(list: nodeptr);
  240.  
  241.     { This procedure serves the function of writing out the list for us
  242.       to the screen when called }
  243.  
  244.     var
  245.       p: nodeptr;
  246.     begin
  247.       p := list;
  248.       while p <> nil do { while we're not at the end of the list }
  249.         begin
  250.           write(p^.thenum:3);
  251.           p := p^.nextnode;
  252.         end;
  253.     end;
  254.  
  255.   procedure insert(var list: nodeptr);
  256.  
  257.     { This procedure will serve to insert a node into the list either in
  258.       the middle or the end.  The logic can be done for the head of the
  259.       list. }
  260.  
  261.     var
  262.       p: nodeptr;
  263.     begin
  264.       new(p);
  265.       p^.thenum := 20;
  266.       p^.nextnode := list;
  267.       if p^.nextnode = nil then  { maintenance of the end of list marker }
  268.         p^.nextnode^.nextnode := nil;
  269.       list := p;
  270.     end;
  271.  
  272.   procedure delete(var list: nodeptr);
  273.     { This is a procedure that will serve to delete a node from the list,
  274.       and consequently deallocate the memory.  It is possible to remove
  275.       the node without deallocating the memory, though it is a bad practice
  276.       to do so }
  277.  
  278.     var
  279.       p, t: nodeptr;
  280.     begin
  281.       p := list;
  282.       t := p^.nextnode^.nextnode;
  283.       dispose(p^.nextnode);
  284.       p^.nextnode := t;
  285.     end;
  286.  
  287.   procedure insertbythree(var list: nodeptr);
  288.  
  289.     { This procedure moves through the linked list and determines where
  290.       the new nodes needs to be inserted, then calls the insert function
  291.       written before }
  292.  
  293.     var
  294.       p: nodeptr;
  295.       i: integer;
  296.     begin
  297.       p := list;
  298.       i := 1;
  299.       while p <> nil do
  300.         begin
  301.           p := p^.nextnode;
  302.           inc(i);
  303.           if i mod 3 = 0 then
  304.             insert(p^.nextnode);
  305.         end;
  306.     end;
  307.  
  308.   procedure findanddispose(var list: nodeptr);
  309.  
  310.     { This procedure finds and disposes the first number in the list
  311.       divisible by 8. }
  312.  
  313.     var
  314.       p, t: nodeptr;
  315.  
  316.     begin
  317.       p := list;
  318.       while (p^.nextnode <> nil) and (p^.nextnode^.thenum mod 8 <> 0) do
  319.         p := p^.nextnode;
  320.       delete(p);
  321.     end;
  322.  
  323.   procedure cleanup(var list: nodeptr);
  324.  
  325.     { This procedure removes the list from memory. }
  326.  
  327.     var
  328.       p, t: nodeptr;
  329.     begin
  330.       p := list;
  331.       while p <> nil do
  332.         begin
  333.           t := p^.nextnode;
  334.           dispose(p);
  335.           p := t;
  336.         end;
  337.     end;
  338.  
  339.   begin
  340.     clrscr;
  341.     writeln;writeln;
  342.     writeln('Free memory available: ', memavail, ' bytes.');
  343.     buildlist(list);
  344.     writeln('Free memory available: ', memavail, ' bytes.');
  345.     write('The list is: ');
  346.     writelist(list);
  347.     writeln;writeln;
  348.     writeln('Now we will insert a 20 in every third position');
  349.     insertbythree(list);
  350.     writeln('Free memory available: ', memavail, ' bytes.');
  351.     write('The list is: ');
  352.     writelist(list);
  353.     writeln;writeln;
  354.     write('Now we will search for and take the first # divisible by 8 ');
  355.     writeln('out of the list.');
  356.     findanddispose(list);
  357.     writeln('Free memory available: ', memavail, ' bytes.');
  358.     write('The list is: ');
  359.     writelist(list);
  360.     writeln;writeln;
  361.     writeln('Now we will be good little programmers and clean up our list. :)');
  362.     cleanup(list);
  363.     writeln('Free memory available: ', memavail, ' bytes.');
  364.   end.
  365.  
  366. Hopefully, you can go through here, and follow the logic (actually, you will
  367. need to do that successfully to understand what is going on).
  368.  
  369. Linked lists are very modular in nature.  Therefore, a good understanding
  370. of what is going on here is essential.  As a proof to be able to think
  371. through the logic of using pointers in linked structures, write out and
  372. logically explain on your sheet of paper how to perform the following
  373. (I will not provide a solution for this one -- code it up yourself and
  374. try and figure it out -- this is an important skill you will need to start
  375. developing as a programmer at this point, since you're at a pretty advanced
  376. level now (:))), and then code it up as a program:
  377.  
  378. 1) Create 1000 nodes in an SLLL that consists of integers numbered from 1
  379. to 1000.
  380. 2) Print this list to a text file.
  381. 3) Reverse the direction of the linked list.  By doing this, I mean, instead
  382. of the linked list looking like this conceptually:
  383.  
  384.                    NODE-->NODE-->NODE-->nil
  385.  
  386. make it look like this:
  387.  
  388.                    nil<--NODE<--NODE<--NODE
  389.  
  390. DO NOT CREATE ANOTHER LINKED LIST IN MEMORY.  USE THE CURRENT ONE YOU HAVE
  391. BUILT.
  392.  
  393. 4) Print the new list to the same text file.  Instead of it being from 1
  394. to 1000 as the first printing was, it should be from 1000 to 1.
  395.  
  396. CLUE: Think about how many temporary variables you will need (1?  Maybe 2?,
  397. Possibly 3?).
  398.  
  399. SLCL Concepts
  400. =============
  401. This is essentially the same as an SLLL, except instead of being nil at the
  402. end of the list, the end of the list points at the beginning of the list.
  403. This type uses the same record format as the SLLL.
  404.  
  405. Conceptually, an SLCL looks like this:
  406.  
  407.   NODE--->NODE--->NODE--->NODE--->NODE
  408.    ^                               |
  409.    |                               V
  410.   NODE                            NODE
  411.    ^                               |
  412.    |                               V
  413.   NODE<---NODE<---NODE<---NODE<---NODE
  414.  
  415. As before with the SLLL, one of these nodes would be denoted as the head
  416. of the list.
  417.  
  418. The only consideration that would differ that I could note, is that you
  419. would use a comparison of your temporary pointer with your head pointer
  420. in order to move through the list.
  421.  
  422. So instead of while p <> nil, it would be while p <> list in the above
  423. example to make it that way.
  424.  
  425. DLLL Concepts
  426. =============
  427. This type of linked list uses a different kind of record format.  It looks
  428. like this:
  429.  
  430. type
  431.   nodeptr = ^node;
  432.   node = record
  433.     ourinfo: integer;
  434.     lastnode, nextnode: nodeptr;
  435.   end;
  436.  
  437. Conceptually, a DLLL looks like this:
  438.  
  439.                 nil <--    <--    <--    <--
  440.                        NODE   NODE   NODE   NODE
  441.                            -->    -->    -->    --> nil
  442.  
  443. If you study up your logic from previously, this one shouldn't be too awfully
  444. bad to figure out.
  445.  
  446. DLCL Concepts
  447. =============
  448. This type of list uses the same record format as the DLLL.  The conceptual
  449. diagram looks much like the SLCL diagram, but with double links much like
  450. the DLLL diagram, instead of single links.
  451.  
  452. Final Thoughts on Linked Lists
  453. ==============================
  454. I did not provide examples of SLCLs, DLLLs, and DLCLs , merely for space,
  455. and also by the fact that I have never had reason to use the other three
  456. types.  I am presenting their basics here, merely for people's study, and
  457. learning.  Using the knowledge learned from doing those logic problems
  458. presented in the SLLLs, and references (though I find those to be VERY
  459. sparse on the types other than SLLL), you should be able to come up with
  460. the code to do the other three types pretty readily.  Always remember that
  461. the best thing to do to work out the logic of what to do with the pointers
  462. is to draw it out using the boxes and the arrows.
  463.  
  464. An Idea on Sorting Data Using Linked Lists
  465. ==========================================
  466. Here, I will now present the reasoning behind my "recursion" statement,
  467. plus an idea of sorting data upon build.  I don't have any stats on
  468. this being more or less efficient than using one of the array sorts,
  469. but if you can't use an array to sort in memory, you would have to resort
  470. to this.
  471.  
  472. Here is a little code/pseudocode (with a bent toward sorting names
  473. alphabetically)  For purposes of the recursion, we will call the
  474. function INSERT:
  475.  
  476. IF WE ARE TO PUT NODE HERE
  477.   GET DATA TO PUT INTO NODE (read data from file, or elsewhere)
  478. WHILE DATA IS NOT DONE DO
  479.   BEGIN
  480.     IF NIL LIST THEN
  481.       PUT NODE HERE
  482.     ELSE
  483.       PUT NODE HERE = NEWNAME <= NODE^.NAME
  484.     IF WE ARE TO PUT NODE HERE THEN
  485.       BEGIN
  486.         new(p);
  487.         SET DATA TO NODE
  488.         p^.nextnode := LIST;
  489.         if p^.nextnode = nil then
  490.           p^.nextnode^.nextnode = nil;
  491.         list := p;
  492.       END
  493.     ELSE
  494.       INSERT(LIST^.NEXTNODE);
  495.     IF WE ARE TO PUT NODE HERE THEN
  496.       BEGIN
  497.         GET INFO.
  498.         DO NOT PUT NODE HERE (boolean variable set to false).
  499.       END.
  500.   END.
  501.  
  502. This general code does work for a high capacity.  I have used this code 
  503. to sort a maximum of an 86KB list of 150 char items per line alphabetically
  504. using memory alone, no disk swapping.
  505.  
  506. For practice: Do things as I have suggested throughout this document.
  507. No real practice problem.
  508.  
  509. Next Time
  510. =========
  511. We will talk about binary trees.  Be sure to send comments to
  512. ggrotz@2sprint.net.  I will say again that I apologize for the long
  513. period of time it took to get this out.  I also apologize for the length
  514. this document has become.  Be sure to please comment on how this
  515. part is.
  516.